home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / options.lha / options / options.C < prev    next >
C/C++ Source or Header  |  1993-04-13  |  28KB  |  958 lines

  1. // ****************************************************************************
  2. // ^FILE: options.c - implement the functions defined in <options.h>
  3. //
  4. // ^HISTORY:
  5. //    01/16/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  6. //
  7. //    03/23/93    Brad Appleton    <brad@ssd.csd.harris.com>
  8. //    - Added OptIstreamIter class
  9. // ^^**************************************************************************
  10.  
  11. #include <stdlib.h>
  12. #include <iostream.h>
  13. #include <ctype.h>
  14. #include <string.h>
  15.  
  16. #include "options.h"
  17.  
  18. extern "C" {
  19.    void  exit(int);
  20. }
  21.  
  22. static const char ident[] = "@(#)Options  1.00" ;
  23.  
  24.    // I need a portable version of "tolower" that does NOT modify
  25.    // non-uppercase characters.
  26.    //
  27. #define  TOLOWER(c)  (isupper(c) ? tolower(c) : c)
  28.  
  29. // ************************************************************** OptIter
  30.  
  31. OptIter::~OptIter(void) {}
  32.  
  33. const char *
  34. OptIter::operator()(void)  {
  35.    const char * elt = curr();
  36.    (void) next();
  37.    return  elt;
  38. }
  39.  
  40. // ************************************************************** OptIterRwd
  41.  
  42. OptIterRwd::OptIterRwd(void) {}
  43.  
  44. OptIterRwd::~OptIterRwd(void) {}
  45.  
  46. // ************************************************************** OptArgvIter
  47.  
  48. OptArgvIter::~OptArgvIter(void) {}
  49.  
  50. const char *
  51. OptArgvIter::curr(void) {
  52.    return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx];
  53. }
  54.  
  55. void
  56. OptArgvIter::next(void) {
  57.    if ((ndx != ac) && av[ndx]) ++ndx;
  58. }
  59.  
  60. const char *
  61. OptArgvIter::operator()(void) {
  62.    return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx++];
  63. }
  64.  
  65. void
  66. OptArgvIter::rewind(void) { ndx = 0; }
  67.  
  68. // ************************************************************** OptStrTokIter
  69.  
  70. static const char WHITESPACE[] = " \t\n\r\v\f" ;
  71. const char * OptStrTokIter::default_delims = WHITESPACE ;
  72.  
  73. OptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters)
  74.    : len(unsigned(strlen(tokens))), str(tokens), seps(delimiters),
  75.      cur(NULL), tokstr(NULL)
  76. {
  77.    if (seps == NULL)  seps = default_delims;
  78.    tokstr = new char[len + 1];
  79.    (void) ::strcpy(tokstr, str);
  80.    cur = ::strtok(tokstr, seps);
  81. }
  82.  
  83.  
  84. OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; }
  85.  
  86. const char *
  87. OptStrTokIter::curr(void) { return cur; }
  88.  
  89. void
  90. OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); }
  91.  
  92. const char *
  93. OptStrTokIter::operator()(void) {
  94.    const char * elt = cur;
  95.    if (cur) cur = ::strtok(NULL, seps);
  96.    return  elt;
  97. }
  98.  
  99. void
  100. OptStrTokIter::rewind(void) {
  101.    (void) ::strcpy(tokstr, str);
  102.    cur = ::strtok(tokstr, seps);
  103. }
  104.  
  105. // ************************************************************* OptIstreamIter
  106.  
  107. const unsigned  OptIstreamIter::MAX_LINE_LEN = 1024 ;
  108.  
  109.    // Constructor
  110. OptIstreamIter::OptIstreamIter(istream & input) : is(input), tok_iter(NULL)
  111. {
  112. }
  113.  
  114.    // Destructor
  115. OptIstreamIter::~OptIstreamIter(void)
  116. {
  117.    delete  tok_iter;
  118. }
  119.  
  120. const char *
  121. OptIstreamIter::curr(void) {
  122.    const char * result = NULL;
  123.    if (tok_iter)  result = tok_iter->curr();
  124.    if (result)  return  result;
  125.    fill();
  126.    return (! is) ? NULL : tok_iter->curr();
  127. }
  128.  
  129. void
  130. OptIstreamIter::next(void) {
  131.    const char * result = NULL;
  132.    if (tok_iter)  result = tok_iter->operator()();
  133.    if (result)  return;
  134.    fill();
  135.    if (! is) tok_iter->next();
  136. }
  137.  
  138. const char *
  139. OptIstreamIter::operator()(void)
  140. {
  141.    const char * result = NULL;
  142.    if (tok_iter)  result = tok_iter->operator()();
  143.    if (result)  return  result;
  144.    fill();
  145.    return (! is) ? NULL : tok_iter->operator()();
  146. }
  147.  
  148.    // What we do is this: for each line of text in the istream, we use
  149.    // a OptStrTokIter to iterate over each token on the line.
  150.    //
  151.    // If the first non-white character on a line is c_COMMENT, then we
  152.    // consider the line to be a comment and we ignore it.
  153.    //
  154. void
  155. OptIstreamIter::fill(void) {
  156.    char buf[OptIstreamIter::MAX_LINE_LEN];
  157.    do {
  158.       *buf = '\0';
  159.       is.getline(buf, sizeof(buf));
  160.       char * ptr = buf;
  161.       while (isspace(*ptr)) ++ptr;
  162.       if (*ptr && (*ptr != OptIstreamIter::c_COMMENT)) {
  163.          delete  tok_iter;
  164.          tok_iter = new OptStrTokIter(ptr);
  165.          return;
  166.       }
  167.    } while (is);
  168. }
  169.  
  170. // ******************************************************************* Options
  171.  
  172. // ---------------------------------------------------------------------------
  173. // ^FUNCTION: verify - verify the syntax of each option-spec
  174. //
  175. // ^SYNOPSIS:
  176. //    static void verify(name, optv[])
  177. //
  178. // ^PARAMETERS:
  179. //    const char * name - name of this command
  180. //    const char * const optv[] - the vector of option-specs to inspect.
  181. //
  182. // ^DESCRIPTION:
  183. //    All we have to do is iterate through the option vector and make sure
  184. //    That each option-spec is of the proper format.
  185. //
  186. // ^REQUIREMENTS:
  187. //    - optv[] should be non-NULL and terminated by a NULL pointer.
  188. //
  189. // ^SIDE-EFFECTS:
  190. //    If an invalid option-spec is found, prints a message on cerr and
  191. //    exits with a status of 127.
  192. //
  193. // ^RETURN-VALUE:
  194. //    None.
  195. //
  196. // ^ALGORITHM:
  197. //    For each option-spec
  198. //       - ensure (length > 0)
  199. //       - verify the the second character is one of "|?:*+"
  200. //    end-for
  201. // ^^-------------------------------------------------------------------------
  202. static void
  203. verify(const char * name, const char * const optv[])
  204. {
  205.    int errors = 0;
  206.    if ((optv == NULL) || (! *optv))  return;
  207.  
  208.    for (; *optv ; optv++) {
  209.       char *p;
  210.       if (! **optv) {
  211.          cerr << name << ": invalid option spec \"" << *optv << "\"." << endl ;
  212.           cerr << "\tmust be at least 1 character long." << endl ;
  213.          ++errors;
  214.       }
  215.       if ((**optv) && ((*optv)[1]) &&
  216.           ((p = strchr("|?:*+", (*optv)[1])) == NULL)) {
  217.          cerr << name << ": invalid option spec \"" << *optv << "\"." << endl ;
  218.          cerr << "\t2nd character must be in the set \"|?:*+\"." << endl ;
  219.          ++errors;
  220.       }
  221.    }/*for*/
  222.  
  223.    if (errors)  exit(127);
  224. }
  225.  
  226. Options::Options(const char * name, const char * const optv[])
  227.    : cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT),
  228.      nextchar(NULL), listopt(NULL)
  229. {
  230.    const char * basename = ::strrchr(cmdname, '/');
  231.    if (basename)  cmdname = basename + 1;
  232.    verify(name, optv);
  233. }
  234.  
  235. Options::~Options(void) {}
  236.  
  237.    // return values for a keyword matching function
  238. enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ;
  239.  
  240.    // Get the option-char of an option-spec.
  241. inline static char
  242. OptChar(const char * optspec) { return  *optspec; }
  243.  
  244.    // Get the long-option of an option-spec.
  245. inline static const char *
  246. LongOpt(const char * optspec) {
  247.     return  ((optspec)[1] && (optspec)[2] &&
  248.              (! isspace((optspec)[2]))) ? ((optspec) + 2) : NULL ;
  249. }
  250.  
  251.    // Is the option-char null?
  252. inline static int
  253. isNullOpt(char optchar) {
  254.    return  ((! optchar) || isspace(optchar) || (! isprint(optchar))) ;
  255. }
  256.  
  257.    // Does this option require an argument?
  258. inline static int
  259. isRequired(const char * optspec) {
  260.    return  (((optspec)[1] == ':') || ((optspec)[1] == '+')) ;
  261. }
  262.  
  263.    // Does this option take an optional argument?
  264. inline static int
  265. isOptional(const char * optspec) {
  266.    return  (((optspec)[1] == '?') || ((optspec)[1] == '*')) ;
  267. }
  268.  
  269.    // Does this option take no arguments?
  270. inline static int
  271. isNoArg(const char * optspec) {
  272.    return  (((optspec)[1] == '|') || (! (optspec)[1])) ;
  273. }
  274.  
  275.    // Can this option take more than one argument?
  276. inline static int
  277. isList(const char * optspec) {
  278.    return  (((optspec)[1] == '+') || ((optspec)[1] == '*')) ;
  279. }
  280.  
  281.    // Does this option take any arguments?
  282. inline static int
  283. isValTaken(const char * optspec) {
  284.    return  (isRequired(optspec) || isOptional(optspec)) ;
  285. }
  286.  
  287.    // Check for explicit "end-of-options"
  288. inline static int
  289. isEndOpts(const char * token)
  290. {
  291.    return ((token == NULL) || (! strcmp(token, "--"))) ;
  292. }
  293.  
  294.    // See if an argument is an option
  295. inline static int
  296. isOption(unsigned  flags, const char * arg)
  297. {
  298.    return  (((*arg != '\0') || (arg[1] != '\0')) &&
  299.             ((*arg == '-')  || ((flags & Options::PLUS) && (*arg == '+')))) ;
  300. }
  301.  
  302. // ---------------------------------------------------------------------------
  303. // ^FUNCTION: kwdmatch - match a keyword
  304. //
  305. // ^SYNOPSIS:
  306. //    static kwdmatch_t kwdmatch(src, attempt, len)
  307. //
  308. // ^PARAMETERS:
  309. //    char * src -- the actual keyword to match
  310. //    char * attempt -- the possible keyword to compare against "src"
  311. //    int len -- number of character of "attempt" to consider
  312. //               (if 0 then we should use all of "attempt")
  313. //
  314. // ^DESCRIPTION:
  315. //    See if "attempt" matches some prefix of "src" (case insensitive).
  316. //
  317. // ^REQUIREMENTS:
  318. //    - attempt should be non-NULL and non-empty
  319. //
  320. // ^SIDE-EFFECTS:
  321. //    None.
  322. //
  323. // ^RETURN-VALUE:
  324. //    An enumeration value of type kwdmatch_t corresponding to whether
  325. //    We had an exact match, a partial match, or no match.
  326. //
  327. // ^ALGORITHM:
  328. //    Trivial
  329. // ^^-------------------------------------------------------------------------
  330. static kwdmatch_t
  331. kwdmatch(const char * src, const char * attempt, int len =0)
  332. {
  333.    int  i;
  334.  
  335.    if (src == attempt)  return  EXACT_MATCH ;
  336.    if ((src == NULL) || (attempt == NULL))  return  NO_MATCH ;
  337.    if ((! *src) && (! *attempt))  return  EXACT_MATCH ;
  338.    if ((! *src) || (! *attempt))  return  NO_MATCH ;
  339.  
  340.    for (i = 0 ; ((i < len) || (len == 0)) &&
  341.                 (attempt[i]) && (attempt[i] != ' ') ; i++) {
  342.       if (TOLOWER(src[i]) != TOLOWER(attempt[i]))  return  NO_MATCH ;
  343.    }
  344.  
  345.    return  (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ;
  346. }
  347.  
  348. // ---------------------------------------------------------------------------
  349. // ^FUNCTION: match_opt - match an option
  350. //
  351. // ^SYNOPSIS:
  352. //    static const char * match_opt(optv, opt)
  353. //
  354. // ^PARAMETERS:
  355. //    char * optv[] -- vector of option-specifications
  356. //    char opt -- the option-character to match
  357. //    int  ignore_case -- should we ignore character-case?
  358. //
  359. // ^DESCRIPTION:
  360. //    See if "opt" is found in "optv"
  361. //
  362. // ^REQUIREMENTS:
  363. //    - optv should be non-NULL and terminated by a NULL pointer.
  364. //
  365. // ^SIDE-EFFECTS:
  366. //    None.
  367. //
  368. // ^RETURN-VALUE:
  369. //    NULL if no match is found,
  370. //    otherwise a pointer to the matching option-spec.
  371. //
  372. // ^ALGORITHM:
  373. //    foreach option-spec
  374. //       - see if "opt" is a match, if so return option-spec
  375. //    end-for
  376. // ^^-------------------------------------------------------------------------
  377. static const char *
  378. match_opt(const char * const optv[], char opt, int ignore_case =0)
  379. {
  380.    if ((optv == NULL) || (! *optv))  return  NULL;
  381.  
  382.    for (; *optv ; ++optv) {
  383.      char optchar = OptChar(*optv);
  384.      if (isNullOpt(optchar))  continue;
  385.      if (opt == optchar) {
  386.         return *optv;
  387.      } else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) {
  388.         return *optv;
  389.      }
  390.    }
  391.  
  392.    return  NULL;  // not found
  393. }
  394.  
  395. // ---------------------------------------------------------------------------
  396. // ^FUNCTION: match_longopt - match a long-option
  397. //
  398. // ^SYNOPSIS:
  399. //   static const char * match_longopt(optv, opt, len, ambiguous)
  400. //
  401. // ^PARAMETERS:
  402. //    char * optv[] -- the vector of option-specs
  403. //    char * opt -- the long-option to match
  404. //    int len -- the number of character of "opt" to match
  405. //    int & ambiguous -- set by this routine before returning.
  406. //
  407. // ^DESCRIPTION:
  408. //    Try to match "opt" against some unique prefix of a long-option
  409. //    (case insensitive).
  410. //
  411. // ^REQUIREMENTS:
  412. //    - optvec should be non-NULL and terminated by a NULL pointer.
  413. //
  414. // ^SIDE-EFFECTS:
  415. //    - *ambiguous is set to '1' if "opt" matches >1 long-option
  416. //      (otherwise it is set to 0).
  417. //
  418. // ^RETURN-VALUE:
  419. //    NULL if no match is found,
  420. //    otherwise a pointer to the matching option-spec.
  421. //
  422. // ^ALGORITHM:
  423. //    ambiguous is FALSE
  424. //    foreach option-spec
  425. //       if we have an EXACT-MATCH, return the option-spec
  426. //       if we have a partial-match then
  427. //          if we already had a previous partial match then 
  428. //             set ambiguous = TRUE and retrun NULL
  429. //          else
  430. //             remember this options spec and continue matching
  431. //          end-if
  432. //       end-if
  433. //    end-for
  434. //    if we had exactly 1 partial match return it, else return NULL
  435. // ^^-------------------------------------------------------------------------
  436. static const char *
  437. match_longopt(const char * const optv[],
  438.               const char       * opt,
  439.               int                len,
  440.               int              & ambiguous)
  441. {
  442.    kwdmatch_t  result;
  443.    const char * matched = NULL ;
  444.  
  445.    ambiguous = 0;
  446.    if ((optv == NULL) || (! *optv))  return  NULL;
  447.  
  448.    for (; *optv ; ++optv) {
  449.       const char * longopt = LongOpt(*optv) ;
  450.       if (longopt == NULL)  continue;
  451.       result = kwdmatch(longopt, opt, len);
  452.       if (result == EXACT_MATCH) {
  453.          return  *optv;
  454.       } else if (result == PARTIAL_MATCH) {
  455.          if (matched) {
  456.             ++ambiguous;
  457.             return  NULL;
  458.          } else {
  459.             matched = *optv;
  460.          }
  461.       }
  462.    }/*for*/
  463.  
  464.    return  matched;
  465. }
  466.  
  467. // ---------------------------------------------------------------------------
  468. // ^FUNCTION: Options::parse_opt - parse an option
  469. //
  470. // ^SYNOPSIS:
  471. //    int Options::parse_opt(iter, optarg)
  472. //
  473. // ^PARAMETERS:
  474. //    OptIter & iter -- option iterator
  475. //    const char * & optarg -- where to store any option-argument
  476. //
  477. // ^DESCRIPTION:
  478. //    Parse the next option in iter (advancing as necessary).
  479. //    Make sure we update the nextchar pointer along the way. Any option
  480. //    we find should be returned and optarg should point to its argument.
  481. //
  482. // ^REQUIREMENTS:
  483. //    - nextchar must point to the prospective option character
  484. //
  485. // ^SIDE-EFFECTS:
  486. //    - iter is advanced when an argument completely parsed
  487. //    - optarg is modified to point to any option argument
  488. //    - if Options::QUIET is not set, error messages are printed on cerr
  489. //
  490. // ^RETURN-VALUE:
  491. //    'c' if the -c option was matched (optarg points to its argument)
  492. //    -1 if the option is invalid (optarg points to the bad option-character).
  493. //
  494. // ^ALGORITHM:
  495. //    It gets complicated -- follow the comments in the source.
  496. // ^^-------------------------------------------------------------------------
  497. int
  498. Options::parse_opt(OptIter & iter, const char * & optarg)
  499. {
  500.    listopt = NULL;  // reset the list pointer
  501.  
  502.    if ((optvec == NULL) || (! *optvec))  return  Options::ENDOPTS;
  503.  
  504.       // Try to match a known option
  505.    const char * optspec = match_opt(optvec, *(nextchar++));
  506.  
  507.       // Check for an unknown option
  508.    if (optspec == NULL) {
  509.       // See if this was a long-option in disguise
  510.       if (! (optctrls & NOGUESSING)) {
  511.          unsigned  save_ctrls = optctrls;
  512.          const char * save_nextchar = nextchar;
  513.          nextchar -= 1;
  514.          optctrls |= (QUIET | NOGUESSING);
  515.          int  optchar = parse_longopt(iter, optarg);
  516.          optctrls = save_ctrls;
  517.          if (optchar > 0) {
  518.             return  optchar;
  519.          } else {
  520.             nextchar = save_nextchar;
  521.          }
  522.       }
  523.       if (! (optctrls & QUIET)) {
  524.          cerr << cmdname << ": unknown option -"
  525.               << *(nextchar - 1) << "." << endl ;
  526.       }
  527.       optarg = (nextchar - 1);  // record the bad option in optarg
  528.       return  BADCHAR;
  529.    }
  530.  
  531.       // If no argument is taken, then leave now
  532.    if (isNoArg(optspec)) {
  533.       optarg = NULL;
  534.       return  OptChar(optspec);
  535.    }
  536.  
  537.       // Check for argument in this arg
  538.    if (*nextchar) {
  539.       optarg = nextchar; // the argument is right here
  540.       nextchar = NULL;   // we've exhausted this arg
  541.       if (isList(optspec))  listopt = optspec ;  // save the list-spec
  542.       return  OptChar(optspec);
  543.    }
  544.  
  545.       // Check for argument in next arg
  546.    const char * nextarg = iter.curr();
  547.    if ((nextarg != NULL)  &&
  548.        (isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
  549.       optarg = nextarg;    // the argument is here
  550.       iter.next();         // end of arg - advance
  551.       if (isList(optspec))  listopt = optspec ;  // save the list-spec
  552.       return  OptChar(optspec);
  553.    }
  554.  
  555.      // No argument given - if its required, thats an error
  556.    optarg = NULL;
  557.    if (isRequired(optspec) &&  !(optctrls & QUIET)) {
  558.       cerr << cmdname << ": argument required for -" << OptChar(optspec)
  559.            << " option." << endl ;
  560.    }
  561.    return  OptChar(optspec);
  562. }
  563.  
  564. // ---------------------------------------------------------------------------
  565. // ^FUNCTION: Options::parse_longopt - parse a long-option
  566. //
  567. // ^SYNOPSIS:
  568. //    int Options::parse_longopt(iter, optarg)
  569. //
  570. // ^PARAMETERS:
  571. //    OptIter & iter -- option iterator
  572. //    const char * & optarg -- where to store any option-argument
  573. //
  574. // ^DESCRIPTION:
  575. //    Parse the next long-option in iter (advancing as necessary).
  576. //    Make sure we update the nextchar pointer along the way. Any option
  577. //    we find should be returned and optarg should point to its argument.
  578. //
  579. // ^REQUIREMENTS:
  580. //    - nextchar must point to the prospective option character
  581. //
  582. // ^SIDE-EFFECTS:
  583. //    - iter is advanced when an argument completely parsed
  584. //    - optarg is modified to point to any option argument
  585. //    - if Options::QUIET is not set, error messages are printed on cerr
  586. //
  587. // ^RETURN-VALUE:
  588. //    'c' if the the long-option corresponding to the -c option was matched
  589. //         (optarg points to its argument)
  590. //    -2 if the option is invalid (optarg points to the bad long-option name).
  591. //
  592. // ^ALGORITHM:
  593. //    It gets complicated -- follow the comments in the source.
  594. // ^^-------------------------------------------------------------------------
  595. int
  596. Options::parse_longopt(OptIter & iter, const char * & optarg)
  597. {
  598.    int  len = 0, ambiguous = 0;
  599.  
  600.    listopt = NULL ;  // reset the list-spec
  601.  
  602.    if ((optvec == NULL) || (! *optvec))  return  Options::ENDOPTS;
  603.  
  604.       // if a value is supplied in this argv element, get it now
  605.    const char * val = strpbrk(nextchar, ":=") ;
  606.    if (val) {
  607.       len = val - nextchar ;
  608.       ++val ;
  609.    }
  610.  
  611.       // Try to match a known long-option
  612.    const char * optspec = match_longopt(optvec, nextchar, len, ambiguous);
  613.  
  614.       // Check for an unknown long-option
  615.    if (optspec == NULL) {
  616.       // See if this was a short-option in disguise
  617.       if ((! ambiguous) && (! (optctrls & NOGUESSING))) {
  618.          unsigned  save_ctrls = optctrls;
  619.          const char * save_nextchar = nextchar;
  620.          optctrls |= (QUIET | NOGUESSING);
  621.          int  optchar = parse_opt(iter, optarg);
  622.          optctrls = save_ctrls;
  623.          if (optchar > 0) {
  624.             return  optchar;
  625.          } else {
  626.             nextchar = save_nextchar;
  627.          }
  628.       }
  629.       if (! (optctrls & QUIET)) {
  630.          cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown")
  631.               << " option "
  632.               << ((optctrls & LONG_ONLY) ? "-" : "--")
  633.               << nextchar << "." << endl ;
  634.       }
  635.       optarg = nextchar;  // record the bad option in optarg
  636.       nextchar = NULL;    // we've exhausted this argument
  637.       return  (ambiguous) ? AMBIGUOUS : BADKWD;
  638.    }
  639.  
  640.       // If no argument is taken, then leave now
  641.    if (isNoArg(optspec)) {
  642.       if ((val) && ! (optctrls & QUIET)) {
  643.          cerr << cmdname << ": option "
  644.               << ((optctrls & LONG_ONLY) ? "-" : "--")
  645.               << LongOpt(optspec) << " does NOT take an argument." << endl ;
  646.       }
  647.       optarg = val;     // record the unexpected argument
  648.       nextchar = NULL;  // we've exhausted this argument
  649.       return  OptChar(optspec);
  650.    }
  651.  
  652.       // Check for argument in this arg
  653.    if (val) {
  654.       optarg = val;      // the argument is right here
  655.       nextchar = NULL;   // we exhausted the rest of this arg
  656.       if (isList(optspec))  listopt = optspec ;  // save the list-spec
  657.       return  OptChar(optspec);
  658.    }
  659.  
  660.       // Check for argument in next arg
  661.    const char * nextarg = iter.curr();  // find the next argument to parse
  662.    if ((nextarg != NULL)  &&
  663.        (isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
  664.       optarg = nextarg;        // the argument is right here
  665.       iter.next();             // end of arg - advance
  666.       nextchar = NULL;         // we exhausted the rest of this arg
  667.       if (isList(optspec))  listopt = optspec ;  // save the list-spec
  668.       return  OptChar(optspec);
  669.    }
  670.  
  671.      // No argument given - if its required, thats an error
  672.    optarg = NULL;
  673.    if (isRequired(optspec) &&  !(optctrls & QUIET)) {
  674.       const char * longopt = LongOpt(optspec);
  675.       const char * spc = ::strchr(longopt, ' ');
  676.       int  longopt_len;
  677.       if (spc) {
  678.          longopt_len = spc - longopt;
  679.       } else {
  680.          longopt_len = ::strlen(longopt);
  681.       }
  682.       cerr << cmdname << ": argument required for "
  683.            << ((optctrls & LONG_ONLY) ? "-" : "--");
  684.       cerr.write(longopt, longopt_len) << " option." << endl ;
  685.    }
  686.    nextchar = NULL;           // we exhausted the rest of this arg
  687.    return  OptChar(optspec);
  688. }
  689.  
  690. // ---------------------------------------------------------------------------
  691. // ^FUNCTION: Options::fmt_opt - format an option-spec for a usage message
  692. //
  693. // ^SYNOPSIS:
  694. //    unsigned Options::fmt_opt(optspec, buf)
  695. //
  696. // ^PARAMETERS:
  697. //    char * optspec -- the option-specification
  698. //    char * buf -- where to print the formatted option
  699. //
  700. // ^DESCRIPTION:
  701. //    Self-explanatory.
  702. //
  703. // ^REQUIREMENTS:
  704. //    - optspec must be a valid option-spec.
  705. //    - buf must be large enough to hold the result
  706. //
  707. // ^SIDE-EFFECTS:
  708. //    - writes to buf.
  709. //
  710. // ^RETURN-VALUE:
  711. //    Number of characters written to buf.
  712. //
  713. // ^ALGORITHM:
  714. //    Trivial.
  715. // ^^-------------------------------------------------------------------------
  716. unsigned
  717. Options::fmt_opt(const char * optspec, char * buf) const
  718. {
  719.    static  char default_value[] = "<value>";
  720.    char optchar = OptChar(optspec);
  721.    const char * longopt = LongOpt(optspec);
  722.    char * p = buf ;
  723.  
  724.    const char * value = NULL;
  725.    int    longopt_len = 0;
  726.    int    value_len = 0;
  727.  
  728.    if (longopt) {
  729.       value = ::strchr(longopt, ' ');
  730.       longopt_len = (value) ? (value - longopt) : ::strlen(longopt);
  731.    } else {
  732.       value = ::strchr(optspec + 1, ' ');
  733.    }
  734.    while (value && (*value == ' '))  ++value;
  735.    if (value && *value) {
  736.       value_len = ::strlen(value);
  737.    } else {
  738.       value = default_value;
  739.       value_len = sizeof(default_value) - 1;
  740.    }
  741.    
  742.    if ((optctrls & SHORT_ONLY) &&
  743.        ((! isNullOpt(optchar)) || (optctrls & NOGUESSING))) {
  744.       longopt = NULL;
  745.    }
  746.    if ((optctrls & LONG_ONLY) && (longopt || (optctrls & NOGUESSING))) {
  747.       optchar = '\0';
  748.    }
  749.    if (isNullOpt(optchar) && (longopt == NULL)) {
  750.       *buf = '\0';
  751.       return  0;
  752.    }
  753.  
  754.    *(p++) = '[';
  755.  
  756.    // print the single character option
  757.    if (! isNullOpt(optchar)) {
  758.       *(p++) = '-';
  759.       *(p++) = optchar;
  760.    }
  761.  
  762.    if ((! isNullOpt(optchar)) && (longopt))  *(p++) = '|';
  763.  
  764.    // print the long option
  765.    if (longopt) {
  766.       *(p++) = '-';
  767.       if (! (optctrls & (LONG_ONLY | SHORT_ONLY))) {
  768.          *(p++) = '-';
  769.       }
  770.       strncpy(p, longopt, longopt_len);
  771.       p += longopt_len;
  772.    }
  773.  
  774.    // print any argument the option takes
  775.    if (isValTaken(optspec)) {
  776.       *(p++) = ' ' ;
  777.       if (isOptional(optspec))  *(p++) = '[' ;
  778.       strcpy(p, value);
  779.       p += value_len;
  780.       if (isList(optspec)) {
  781.          strcpy(p, " ...");
  782.          p += 4;
  783.       }
  784.       if (isOptional(optspec))  *(p++) = ']' ;
  785.    }
  786.  
  787.    *(p++) = ']';
  788.    *p = '\0';
  789.  
  790.    return  (unsigned) strlen(buf);
  791. }
  792.  
  793. // ---------------------------------------------------------------------------
  794. // ^FUNCTION: Options::usage - print usage
  795. //
  796. // ^SYNOPSIS:
  797. //    void Options::usage(os, positionals)
  798. //
  799. // ^PARAMETERS:
  800. //    ostream & os -- where to print the usage
  801. //    char * positionals -- command-line syntax for any positional args
  802. //
  803. // ^DESCRIPTION:
  804. //    Print command-usage (using either option or long-option syntax) on os.
  805. //
  806. // ^REQUIREMENTS:
  807. //    os should correspond to an open output file.
  808. //
  809. // ^SIDE-EFFECTS:
  810. //    Prints on os
  811. //
  812. // ^RETURN-VALUE:
  813. //    None.
  814. //
  815. // ^ALGORITHM:
  816. //    Print usage on os, wrapping long lines where necessary.
  817. // ^^-------------------------------------------------------------------------
  818. void
  819. Options::usage(ostream & os, const char * positionals) const
  820. {
  821.    const char * const * optv = optvec;
  822.    unsigned  cols = 79;
  823.    int  first, nloop;
  824.    char  buf[256] ;
  825.  
  826.    if ((optv == NULL) || (! *optv))  return;
  827.  
  828.       // print first portion "usage: progname"
  829.    os << "usage: " << cmdname ;
  830.    unsigned  ll = strlen(cmdname) + 7;
  831.  
  832.       // save the current length so we know how much space to skip for
  833.       // subsequent lines.
  834.       //
  835.    unsigned  margin = ll + 1;
  836.  
  837.       // print the options and the positional arguments
  838.    for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
  839.       unsigned  len;
  840.  
  841.          // figure out how wide this parameter is (for printing)
  842.       if (! *optv) {
  843.          len = strlen(positionals);
  844.          ++nloop;  // terminate this loop
  845.       } else {
  846.          len = fmt_opt(*optv, buf);
  847.       }
  848.  
  849.       //  Will this fit?
  850.       if ((ll + len + 1) > (cols - first)) {
  851.          os << '\n' ;  // No - start a new line;
  852.          os.width(margin);
  853.          os << "" ;
  854.          ll = margin;
  855.       } else {
  856.          os << ' ' ;  // Yes - just throw in a space
  857.          ++ll;
  858.       }
  859.       ll += len;
  860.       os << ((nloop) ? positionals : buf) ;
  861.    }// for each ad
  862.  
  863.    os << endl ;
  864. }
  865.  
  866.  
  867. // ---------------------------------------------------------------------------
  868. // ^FUNCTION: Options::operator() - get options from the command-line
  869. //
  870. // ^SYNOPSIS:
  871. //   int Options::operator()(iter, optarg)
  872. //
  873. // ^PARAMETERS:
  874. //    OptIter & iter -- option iterator
  875. //    const char * & optarg -- where to store any option-argument
  876. //
  877. // ^DESCRIPTION:
  878. //    Parse the next option in iter (advancing as necessary).
  879. //    Make sure we update the nextchar pointer along the way. Any option
  880. //    we find should be returned and optarg should point to its argument.
  881. //
  882. // ^REQUIREMENTS:
  883. //    None.
  884. //
  885. // ^SIDE-EFFECTS:
  886. //    - iter is advanced when an argument is completely parsed
  887. //    - optarg is modified to point to any option argument
  888. //    - if Options::QUIET is not set, error messages are printed on cerr
  889. //
  890. // ^RETURN-VALUE:
  891. //     0 if all options have been parsed.
  892. //    'c' if the the option or long-option corresponding to the -c option was
  893. //         matched (optarg points to its argument).
  894. //    -1 if the option is invalid (optarg points to the bad option character).
  895. //    -2 if the option is invalid (optarg points to the bad long-option name).
  896. //
  897. // ^ALGORITHM:
  898. //    It gets complicated -- follow the comments in the source.
  899. // ^^-------------------------------------------------------------------------
  900. int
  901. Options::operator()(OptIter & iter, const char * & optarg)
  902. {
  903.    explicit_end = 0;
  904.  
  905.       // See if we have an option left over from before ...
  906.    if ((nextchar) && *nextchar) {
  907.       return  parse_opt(iter, optarg);
  908.    }
  909.  
  910.       // Check for end-of-options ...
  911.    const char * arg = iter.curr();
  912.    if (arg == NULL) {
  913.       listopt = NULL;
  914.       return  ENDOPTS;
  915.    } else if (isEndOpts(arg)) {
  916.       iter.next();   // advance past end-of-options arg
  917.       listopt = NULL;
  918.       explicit_end = 1;
  919.       return  ENDOPTS;
  920.    }
  921.  
  922.       // Do we have a positional arg?
  923.    if (! listopt) {
  924.       if ((! *arg) || (! arg[1])) {
  925.          return  ENDOPTS;
  926.       } else if ((*arg != '-') &&
  927.                  ((! (optctrls & PLUS)) || (*arg != '+'))) {
  928.          return  ENDOPTS;
  929.       }
  930.    }
  931.  
  932.    iter.next();  // pass the argument that arg already points to
  933.  
  934.       // See if we have a long option ...
  935.    if (! (optctrls & SHORT_ONLY)) {
  936.       if ((*arg == '-') && (arg[1] == '-')) {
  937.          nextchar = arg + 2;
  938.          return  parse_longopt(iter, optarg);
  939.       } else if ((optctrls & PLUS) && (*arg == '+')) {
  940.          nextchar = arg + 1;
  941.          return  parse_longopt(iter, optarg);
  942.       }
  943.    }
  944.    if (*arg == '-') {
  945.       nextchar = arg + 1;
  946.       if (optctrls & LONG_ONLY) {
  947.          return  parse_longopt(iter, optarg);
  948.       } else {
  949.          return  parse_opt(iter, optarg);
  950.       }
  951.    }
  952.  
  953.       // If we get here - it is because we have a list value
  954.    optarg = arg ;        // record the list value
  955.    return  OptChar(listopt) ;
  956. }
  957.  
  958.